namespace glf {

////////////////////////////////////////////////////////////////////////////////////////////////////
// Atomic
////////////////////////////////////////////////////////////////////////////////////////////////////
template<typename T>
class AtomicImpl {
public:
	inline AtomicImpl();
	inline AtomicImpl(T const& value);

	inline T fetchAndAdd(T const& value);
	inline T fetchAndSub(T const& value);
	inline T fetchAndOr(T const& value);
	inline T fetchAndAnd(T const& value);
	inline T fetchAndXor(T const& value);
	inline T fetchAndNand(T const& value);

	inline T addAndFetch(T const& value);
	inline T subAndFetch(T const& value);
	inline T orAndFetch(T const& value);
	inline T andAndFetch(T const& value);
	inline T xorAndFetch(T const& value);
	inline T nandAndFetch(T const& value);

	inline T compareAndSwap(const T& value, const T& comparand);

	inline T operator++();
	inline T operator--();
	inline T operator++(int);
	inline T operator--(int);
	inline T operator=(T const& );
	inline operator T() const;

protected:
	T mValue;
};

template<typename T>
T AtomicCompareAndSwap(volatile T& destination, T exchange, T comparand);

template<>
unsigned long AtomicCompareAndSwap<unsigned long>(volatile unsigned long& destination, unsigned long exchange, unsigned long comparand);

template<typename T>
T AtomicIncrement(volatile T& destination);

template<typename T>
T AtomicDecrement(volatile T& destination);

template<typename T>
AtomicImpl<T>::AtomicImpl() {
}

template<typename T>
AtomicImpl<T>::AtomicImpl(T const& value) {
	mValue = value;
}
	
template<typename T>
T AtomicImpl<T>::fetchAndAdd(T const& value) {
	return __sync_fetch_and_add(&mValue, value);
}

template<typename T>
T AtomicImpl<T>::fetchAndSub(T const& value) {
	return __sync_fetch_and_sub(&mValue, value);
}

template<typename T>
T AtomicImpl<T>::fetchAndOr(T const& value) {
	return __sync_fetch_and_or(&mValue, value);
}

template<typename T>
T AtomicImpl<T>::fetchAndAnd(T const& value) {
	return __sync_fetch_and_and(&mValue, value);
}

template<typename T>
T AtomicImpl<T>::fetchAndXor(T const& value) {
	return __sync_fetch_and_xor(&mValue, value);
}

template<typename T>
T AtomicImpl<T>::fetchAndNand(T const& value) {
	return __sync_fetch_and_nand(&mValue, value);
}

template<typename T>
T AtomicImpl<T>::addAndFetch(T const& value) {
	return __sync_add_and_fetch(&mValue, value);
}

template<typename T>
T AtomicImpl<T>::subAndFetch(T const& value) {
	return __sync_sub_and_fetch(&mValue, value);
}

template<typename T>
T AtomicImpl<T>::orAndFetch(T const& value) {
	return __sync_or_and_fetch(&mValue, value);
}

template<typename T>
T AtomicImpl<T>::andAndFetch(T const& value) {
	return __sync_and_and_fetch(&mValue, value);
}

template<typename T>
T AtomicImpl<T>::xorAndFetch(T const& value) {
	return __sync_xor_and_fetch(&mValue, value);
}

template<typename T>
T AtomicImpl<T>::nandAndFetch(T const& value) {
	return __sync_nand_and_fetch(&mValue, value);
}

template<typename T>
T AtomicImpl<T>::compareAndSwap(const T& value, const T& comparand) {
	return AtomicCompareAndSwap(mValue, value, comparand);
}

template<typename T>
T AtomicImpl<T>::operator=(T const& value) {
	mValue = value;
	return value; // don't return mValue, mVlaue could be changed by other atomic operation.
}

template<typename T>
T AtomicImpl<T>::operator ++() {
	return __sync_add_and_fetch(&mValue, T(1));
}

template<typename T>
T AtomicImpl<T>::operator --() {
	return __sync_sub_and_fetch(&mValue, T(1));
}

template<typename T>
T AtomicImpl<T>::operator ++(int) {
	return __sync_fetch_and_add(&mValue, T(1));
}

template<typename T>
T AtomicImpl<T>::operator --(int) {
	return __sync_fetch_and_sub(&mValue, T(1));
}

template<typename T>
AtomicImpl<T>::operator T() const {
	return 	LoadWithAcquire(mValue);
}

#define GLF_ATOMIC_IMPL(type) \
	template<> class Atomic<type>: public AtomicImpl<type> { \
	public: \
		Atomic() {} \
		Atomic(type const& value) : AtomicImpl<type>(value) {} \
	}

GLF_ATOMIC_IMPL(bool);
GLF_ATOMIC_IMPL(char);
GLF_ATOMIC_IMPL(unsigned char);
GLF_ATOMIC_IMPL(short);
GLF_ATOMIC_IMPL(unsigned short);
GLF_ATOMIC_IMPL(int);
GLF_ATOMIC_IMPL(unsigned int);
GLF_ATOMIC_IMPL(long);
GLF_ATOMIC_IMPL(unsigned long);
GLF_ATOMIC_IMPL(long long);
GLF_ATOMIC_IMPL(unsigned long long);
GLF_ATOMIC_IMPL(void*);

#undef GLF_ATOMIC_IMPL

template<typename T> class Atomic<T*> : public AtomicImpl<T*> {
};

typedef Atomic<int> Atomic32;

template<typename T>
inline T AtomicCompareAndSwap(volatile T& destination, T exchange, T comparand)
{
	return __sync_val_compare_and_swap(&destination, comparand, exchange);
}

template<>
inline unsigned long AtomicCompareAndSwap<unsigned long>(volatile unsigned long& destination, unsigned long exchange, unsigned long comparand)
{
    return __sync_val_compare_and_swap(&destination, comparand, exchange);
}

template<typename T>
inline T AtomicIncrement(volatile T& destination)
{
	return __sync_add_and_fetch(&destination, T(1));
}
	
template<typename T>
inline T AtomicDecrement(volatile T& destination)
{
	return __sync_sub_and_fetch(&destination, T(1));
}
	
inline void Membar()
{
	__sync_synchronize();
}

}
